from django.shortcuts import render
from rest_framework.views import APIView
import pickle, base64
from rest_framework.response import Response
from rest_framework import status
from django_redis import get_redis_connection

from .serializers import CartSerializer, CartSKUSerializer, CartDeletedSerializer, CartSelectAllSerializer
from goods.models import SKU


# Create your views here.

class CartView(APIView):
    """购物车增删改查"""
    def perform_authentication(self, request):
        """重写此方法可以延后/禁用认证"""
        # 当第一次写 request.user / request.auth就会再去执行认证逻辑
        pass

    def post(self, request):
        """购物车新增"""
        # 创建序列化器进行反序列化
        serializer = CartSerializer(data=request.data)
        # 调用序列化器的校验方法
        serializer.is_valid(raise_exception=True)
        # 获取反序化校验后的数据
        sku_id = serializer.validated_data.get('sku_id')
        count = serializer.validated_data.get('count')
        selected = serializer.validated_data.get('selected')



        try:
            user = request.user  # 当执行到这行代码时,还会做认证,但是未登录用户认证会抛出401,自己捕获不让它提前响应
        except:
            user = None

        response = Response(serializer.data, status=status.HTTP_201_CREATED)
        # user.is_authenticated() 判断是登录用户还是匿名用户
        if user:
            # 登录用户操作redis购物车数据
            # 创建redis连接对象
            redis_conn = get_redis_connection('cart')
            # 创建管道
            pl = redis_conn.pipeline()
            """
            hash: {sku_id_2: 2, sku_id_5: count, sku_id_3: 1}
            set: {sku_id_2}
            """
            # hincrby()  增量的同时做添加
            # pl.hincrby(name, 3, 1)
            # 如果要添加到key在hash字典中已存在,就会把key对应的value与新添加的value做累加,然后存储累加后的值
            # 如果要添加到key在hash字典中不存在,那么就是一个新增行为
            pl.hincrby('cart_%s' % user.id, sku_id, count)
            # 判断当前商品有没有勾选,如果是勾选再添加到set集合
            if selected:
                # 把勾选商品的sku_id添加到set集合
                pl.sadd('selected_%s' % user.id, sku_id)

            # 执行管道
            pl.execute()

            # 响应
            # return Response(serializer.data, status=status.HTTP_201_CREATED)

        else:
            # 未登录用户操作cookie购物车数据
            # 先获取出cookie购物车数据
            cart_str = request.COOKIES.get('cart')
            # 判断购物车中cookie中是否有数据
            if cart_str:
                # 先把str的购物车数据转换成bytes
                cart_str_bytes = cart_str.encode()
                # 再调用baseb4.b64decode() 把bytes类型的字符串转换成bytes类型的ASCII
                cart_str_ascii = base64.b64decode(cart_str_bytes)
                # 再调用pickle.loads() 把bytes类型的ASCII 转换回 字典
                cart_dict = pickle.loads(cart_str_ascii)
            else:
                cart_dict = {}  # 如果是第一次添加准备一个新字典

            """
            {
                sku_id: {
                    "count": xxx,  // 数量
                    "selected": True  // 是否勾选
                },
                sku_id: {
                    "count": xxx,
                    "selected": False
                },
                ...
            }
            """
            # 判断当前要添加的sku_id在cookie字典中是否已存在,如果已存在,取出原sku_id的count,和本次添加的count累加
            if sku_id in cart_dict:
                origin_count = cart_dict[sku_id]['count']
                count += origin_count  # count = origin_count + count

            # 添加到cookie购物车字典
            cart_dict[sku_id] = {
                'count': count,
                'selected': selected
            }

            # 把cookie字典转换成 bytes类型的ASCII  pickle.dumps()
            cart_str_ascii = pickle.dumps(cart_dict)
            # 调用base64.b64encode() 把bytes类型的ASCII转换成bytes类型的str
            cart_str_bytes = base64.b64encode(cart_str_ascii)
            # 调用decode()把bytes类型str 转换成str
            cart_str = cart_str_bytes.decode()

            # 设置到cookie
            # response = Response(serializer.data, status=status.HTTP_201_CREATED)
            response.set_cookie('cart', cart_str)

            # 响应
        return response

    def get(self, request):
        """购物车查询"""

        try:
            user = request.user
        except:
            user = None

        if user:
            # 登录用户获取redis购物车数据
            # 创建redis连接对象
            redis_conn = get_redis_connection('cart')
            # 取出hash字典中的数据 {sku_id: count}
            cart_redis = redis_conn.hgetall('cart_%s' % user.id)
            # 取出set集合中的数据 {sku_id}
            selected_ids = redis_conn.smembers('selected_%s' % user.id)
            # 定义一个字典, 进行把redis购物车数据转换成和cookie购物车数据格式一样
            cart_dict = {}  # 将来和cookie字典模型及变量名都一样
            for sku_id_bytes, count_bytes in cart_redis.items():
                cart_dict[int(sku_id_bytes)] = {
                    'count': int(count_bytes),
                    'selected': sku_id_bytes in selected_ids
                }

            """
               {
                   sku_id: {
                       "count": xxx,  // 数量
                       "selected": True  // 是否勾选
                   },
                   sku_id: {
                       "count": xxx,
                       "selected": False
                   },
                   ...
               }
            """

        else:
            # 未登录用户获取cookie购物车数据
            # 获取cookie数据
            cart_str = request.COOKIES.get('cart')
            if cart_str:
                # 把cookie字符串转换回字典
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                return Response(status=status.HTTP_400_BAD_REQUEST)

        # 获取sku_id对应的模型并且做相应的序列化
        sku_ids = cart_dict.keys()
        skus = SKU.objects.filter(id__in=sku_ids)
        for sku in skus:
            sku.count = cart_dict[sku.id]['count']  # 在数据后面多加一逗号会自动包成元组
            sku.selected = cart_dict[sku.id]['selected']
        # 响应
        serializer = CartSKUSerializer(skus, many=True)

        return Response(serializer.data)

    def put(self, request):
        """购物车修改"""

        serializer = CartSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        sku_id = serializer.validated_data.get('sku_id')
        count = serializer.validated_data.get('count')
        selected = serializer.validated_data.get('selected')

        try:
            user = request.user
        except:
            user = None

        response = Response(serializer.data)
        if user:
            # 登录用户修改redis购物车数据
            # 创建redis连接对象
            redis_conn = get_redis_connection('cart')
            # 创建管道
            pl = redis_conn.pipeline()
            # 直接设置hash字典  如果sku_id在hash字典中已存在 ,直接新值覆盖旧值
            pl.hset('cart_%s' % user.id, sku_id, count)
            # 判断selected是勾选还是未勾选
            if selected:
                # 如果勾选把当前sku_id添加到set集合中
                pl.sadd('selected_%s' % user.id, sku_id)
            else:
                # 如果未勾选把当前sku_id从set集合中移除
                pl.srem('selected_%s' % user.id, sku_id)
            # 执行管道
            pl.execute()
            # 响应
            # return Response(serializer.data)

        else:
            # 未登录用户修改cookie购物车数据
            # 获取cookie购物车数据
            cart_str = request.COOKIES.get('cart')
            if cart_str:
                # 把字符串类型的购物车数据转换成字典
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                return Response(status=status.HTTP_400_BAD_REQUEST)
            # 对cart_dict 直接重新赋值
            cart_dict[sku_id] = {
                'count': count,
                'selected': selected
            }
            # 把购物车字典转成字符串
            cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
            # 设置cookie

            response.set_cookie('cart', cart_str)
            # 响应
            # return response
        # 响应
        return response




    def delete(self, request):
        """购物车删除"""
        serializer = CartDeletedSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        sku_id = serializer.validated_data.get('sku_id')

        try:
            user = request.user
        except:
            user = None

        response = Response(status=status.HTTP_204_NO_CONTENT)

        if user:
            """登录用户操作redis购物车数据"""
            # 创建redis连接对象
            redis_conn = get_redis_connection('cart')
            # 创建管道对象
            pl = redis_conn.pipeline()
            # 把当前要删除的sku_id 从hash字典中移除
            pl.hdel('cart_%s' % user.id, sku_id)
            # 把当前要删除的sku_id 从set集合中移除
            pl.srem('selected_%s' % user.id, sku_id)
            # 执行管道
            pl.execute()
            # 响应
            # return Response(status=status.HTTP_204_NO_CONTENT)

        else:
            """未登录用户操作cookie购物车数据"""
            # 获取请求对象的cookie购物车数据
            cart_str = request.COOKIES.get('cart')
            if cart_str:  # 如果cookie获取出来了
                # 把str车换成 dict
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                # cart_dict = {}
                return Response(status=status.HTTP_400_BAD_REQUEST)
            # 判断当前要删除的sku_id在字典中是否存在
            if sku_id in cart_dict:
                # del dict[sku_id]
                del cart_dict[sku_id]
            # 把dict 转换成 str
            cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
            # 设置cookie覆盖原有cookie数据
            response.set_cookie('cart', cart_str)
        # 响应
        return response


class CartSelectAllView(APIView):
    """购物车全选"""

    def perform_authentication(self, request):
        """重写此方法延后认证"""
        pass

    def put(self, request):
        # 创建序列化器进行反序列化校验
        serializer = CartSelectAllSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        selected = serializer.validated_data.get('selected')

        # 获取user
        try:
            user = request.user
        except:
            user = None

        response = Response(serializer.data)
        if user:
            """登录用户操作redis购物车"""
            # 创建redis连接
            redis_conn = get_redis_connection('cart')
            # 获取hash中所有购物车商品sku_id: count {b'1': b'2', b'16': b'5'}
            # cart_redis_dict = redis_conn.hgetall('cart_%s' % user.id)
            # # 把hash字典中所有的key获取出来
            # sku_ids = cart_redis_dict.keys()
            # hkeys() 获取hash字典中的所有key
            sku_ids = redis_conn.hkeys('cart_%s' % user.id)
            # 如果是全选
            if selected:
                # 把所有sku_id添加到set集合中
                redis_conn.sadd('selected_%s' % user.id, *sku_ids)
            # 取消全选
            else:
                # 把所有sku_id从set集合中移除
                redis_conn.srem('selected_%s' % user.id, *sku_ids)
            # 响应
            # return Response(serializer.data)

        else:
            """未登录用户操作cookie购物车"""
            # 获取cookie中的购物车数据
            cart_str = request.COOKIES.get('cart')
            # 判断cookie是否有购物车数据
            if cart_str:
                # 把str 转换成 dict
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                # 如果cookie没有获取出来,提前响应 400
                return Response(status=status.HTTP_400_BAD_REQUEST)

            # 遍历cart_dict字典对内部的小字典中的selected key对应的value进行修改
            for sku_id in cart_dict:
                cart_dict[sku_id]['selected'] = selected
            # {i for i in range(5) if i % 2 != 0}
            # {key: value for if for if}
            # cart_dict = {sku_id: {'count': cart_dict[sku_id]['count'], 'selected': selected} for sku_id in cart_dict}
            # 把dict 转换成 str
            cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
            # 设置cookie
            response.set_cookie('cart', cart_str)

        # 响应
        return response
